/*
 * QEMU model of the PMX_ANLG PMX Analog
 *
 * Copyright (C) 2022, Xilinx, Inc.
 * Copyright (C) 2023-2024, Advanced Micro Devices, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/* Autogenerated by xregqemu.py 2022-10-18.  */

#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/register.h"
#include "qemu/bitops.h"
#include "qemu/log.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "migration/vmstate.h"
#include "hw/irq.h"
#include "hw/qdev-properties.h"
#include "xlnx-versal-ams.h"


#ifndef XILINX_PMX_ANLG_ERR_DEBUG
#define XILINX_PMX_ANLG_ERR_DEBUG 0
#endif

#define TYPE_XILINX_PMX_ANLG "xlnx.pmx_anlg"
#define TYPE_XILINX_PMXC_ANLG "xlnx.pmxc_anlg"

#define XILINX_PMX_ANLG(obj) \
     OBJECT_CHECK(PMX_ANLG, (obj), TYPE_XILINX_PMX_ANLG)

REG32(GD_CTRL, 0x0)
    FIELD(GD_CTRL, GD1_RST_STATUS_REG, 25, 1)
    FIELD(GD_CTRL, GD1_FABRIC_GL_EN, 24, 1)
    FIELD(GD_CTRL, GD1_TEST_GLITCH_SEL, 19, 5)
    FIELD(GD_CTRL, GD1_TEST_GLITCH_GEN, 18, 1)
    FIELD(GD_CTRL, GD1_GL_DET_TEST_MODE, 17, 1)
    FIELD(GD_CTRL, GD1_EN_GLITCH_DET_B, 16, 1)
    FIELD(GD_CTRL, GD0_RST_STATUS_REG, 9, 1)
    FIELD(GD_CTRL, GD0_FABRIC_GL_EN, 8, 1)
    FIELD(GD_CTRL, GD0_TEST_GLITCH_SEL, 3, 5)
    FIELD(GD_CTRL, GD0_TEST_GLITCH_GEN, 2, 1)
    FIELD(GD_CTRL, GD0_GL_DET_TEST_MODE, 1, 1)
    FIELD(GD_CTRL, GD0_EN_GLITCH_DET_B, 0, 1)
REG32(GLITCH_DET_STATUS, 0x4)
    FIELD(GLITCH_DET_STATUS, VCCINT_PMC_1, 1, 1)
    FIELD(GLITCH_DET_STATUS, VCCINT_PMC_0, 0, 1)
REG32(VGG_CTRL, 0xc)
    FIELD(VGG_CTRL, TEST_VGG_SEL, 14, 5)
    FIELD(VGG_CTRL, TEST_REF_SEL, 9, 5)
    FIELD(VGG_CTRL, TEST_VGG_VDD_SEL, 7, 2)
    FIELD(VGG_CTRL, TEST_VGG_VDD_EN, 6, 1)
    FIELD(VGG_CTRL, TEST_VGG_EN, 5, 1)
    FIELD(VGG_CTRL, TEST_NEG_SLOPE_VGG, 4, 1)
    FIELD(VGG_CTRL, SW_DIS_VGG_REG, 1, 1)
    FIELD(VGG_CTRL, EN_VGG_CLAMP, 0, 1)
REG32(CFRM_PROBE, 0x10)
    FIELD(CFRM_PROBE, MUX_SELECT, 0, 20)
REG32(PMC_SYSMON, 0x14)
    FIELD(PMC_SYSMON, AMS_I2C_SEL, 0, 1)
REG32(GD_FUSE_CTRL_0, 0x20)
    FIELD(GD_FUSE_CTRL_0, SPARE_SEL, 16, 4)
    FIELD(GD_FUSE_CTRL_0, VCCINT_PMC_VAL_SEL, 12, 3)
    FIELD(GD_FUSE_CTRL_0, DEL_SEL, 8, 4)
    FIELD(GD_FUSE_CTRL_0, COMP_SEL, 4, 2)
    FIELD(GD_FUSE_CTRL_0, USE_REG, 0, 1)
REG32(GD_FUSE_CTRL_1, 0x24)
    FIELD(GD_FUSE_CTRL_1, SPARE_SEL, 16, 4)
    FIELD(GD_FUSE_CTRL_1, VCCINT_PMC_VAL_SEL, 12, 3)
    FIELD(GD_FUSE_CTRL_1, DEL_SEL, 8, 4)
    FIELD(GD_FUSE_CTRL_1, COMP_SEL, 4, 2)
    FIELD(GD_FUSE_CTRL_1, USE_REG, 0, 1)
REG32(CFG_POR_CNT_SKIP, 0x30)
    FIELD(CFG_POR_CNT_SKIP, VAL, 0, 1)
REG32(PMC_ANLG_ISR, 0x40)
    FIELD(PMC_ANLG_ISR, SLVERR, 0, 1)
REG32(PMC_ANLG_IMR, 0x44)
    FIELD(PMC_ANLG_IMR, SLVERR, 0, 1)
REG32(PMC_ANLG_IER, 0x48)
    FIELD(PMC_ANLG_IER, SLVERR, 0, 1)
REG32(PMC_ANLG_IDR, 0x4c)
    FIELD(PMC_ANLG_IDR, SLVERR, 0, 1)
REG32(SLVERR_CTRL, 0x50)
    FIELD(SLVERR_CTRL, ENABLE, 0, 1)
REG32(BNK3_VMODE, 0x1004c)
    FIELD(BNK3_VMODE, BNK3_VMODE, 0, 1)
REG32(LDO_CALIB_TIME, 0x100f0)
REG32(BISR_CACHE_CTRL_0, 0x20000)
    FIELD(BISR_CACHE_CTRL_0, CLR, 4, 1)
    FIELD(BISR_CACHE_CTRL_0, TRIGGER, 0, 1)
REG32(BISR_CACHE_STATUS, 0x20008)
    FIELD(BISR_CACHE_STATUS, PASS, 1, 1)
    FIELD(BISR_CACHE_STATUS, DONE, 0, 1)
REG32(MEM_CLEAR_TRIGGER, 0x20100)
    FIELD(MEM_CLEAR_TRIGGER, LPD_RPU_B, 8, 1)
    FIELD(MEM_CLEAR_TRIGGER, LPD_RPU_A, 7, 1)
    FIELD(MEM_CLEAR_TRIGGER, LPD_IOU, 6, 1)
    FIELD(MEM_CLEAR_TRIGGER, LPD_RPU, 5, 1)
    FIELD(MEM_CLEAR_TRIGGER, LPD, 4, 1)
    FIELD(MEM_CLEAR_TRIGGER, PMC_IOU, 1, 1)
    FIELD(MEM_CLEAR_TRIGGER, PMC, 0, 1)
REG32(MEM_CLEAR_DONE, 0x20110)
    FIELD(MEM_CLEAR_DONE, LPD_RPU_B, 8, 1)
    FIELD(MEM_CLEAR_DONE, LPD_RPU_A, 7, 1)
    FIELD(MEM_CLEAR_DONE, LPD_IOU, 6, 1)
    FIELD(MEM_CLEAR_DONE, LPD_RPU, 5, 1)
    FIELD(MEM_CLEAR_DONE, LPD, 4, 1)
    FIELD(MEM_CLEAR_DONE, PMC_IOU, 1, 1)
    FIELD(MEM_CLEAR_DONE, PMC, 0, 1)
REG32(MEM_CLEAR_PASS, 0x20114)
    FIELD(MEM_CLEAR_PASS, LPD_RPU_B, 8, 1)
    FIELD(MEM_CLEAR_PASS, LPD_RPU_A, 7, 1)
    FIELD(MEM_CLEAR_PASS, LPD_IOU, 6, 1)
    FIELD(MEM_CLEAR_PASS, LPD_RPU, 5, 1)
    FIELD(MEM_CLEAR_PASS, LPD, 4, 1)
    FIELD(MEM_CLEAR_PASS, PMC_IOU, 1, 1)
    FIELD(MEM_CLEAR_PASS, PMC, 0, 1)
REG32(SCAN_CLEAR_TRIGGER, 0x20120)
    FIELD(SCAN_CLEAR_TRIGGER, LPD_RPU_B, 10, 1)
    FIELD(SCAN_CLEAR_TRIGGER, LPD_RPU_A, 9, 1)
    FIELD(SCAN_CLEAR_TRIGGER, NOC, 8, 1)
    FIELD(SCAN_CLEAR_TRIGGER, LPD_IOU, 6, 1)
    FIELD(SCAN_CLEAR_TRIGGER, LPD_RPU, 5, 1)
    FIELD(SCAN_CLEAR_TRIGGER, LPD, 4, 1)
REG32(SCAN_CLEAR_LOCK, 0x20124)
    FIELD(SCAN_CLEAR_LOCK, LOCK, 0, 1)
REG32(SCAN_CLEAR_DONE, 0x20128)
    FIELD(SCAN_CLEAR_DONE, LPD_RPU_B, 8, 1)
    FIELD(SCAN_CLEAR_DONE, LPD_RPU_A, 7, 1)
    FIELD(SCAN_CLEAR_DONE, LPD_IOU, 6, 1)
    FIELD(SCAN_CLEAR_DONE, LPD_RPU, 5, 1)
    FIELD(SCAN_CLEAR_DONE, LPD, 4, 1)
    FIELD(SCAN_CLEAR_DONE, PMC, 0, 1)
REG32(SCAN_CLEAR_PASS, 0x2012c)
    FIELD(SCAN_CLEAR_PASS, LPD_RPU_B, 8, 1)
    FIELD(SCAN_CLEAR_PASS, LPD_RPU_A, 7, 1)
    FIELD(SCAN_CLEAR_PASS, LPD_IOU, 6, 1)
    FIELD(SCAN_CLEAR_PASS, LPD_RPU, 5, 1)
    FIELD(SCAN_CLEAR_PASS, LPD, 4, 1)
    FIELD(SCAN_CLEAR_PASS, PMC, 0, 1)
REG32(LBIST_ENABLE, 0x20200)
    FIELD(LBIST_ENABLE, LPD_RPU, 1, 1)
    FIELD(LBIST_ENABLE, LPD, 0, 1)
REG32(LBIST_RST_N, 0x20204)
    FIELD(LBIST_RST_N, LPD_RPU, 1, 1)
    FIELD(LBIST_RST_N, LPD, 0, 1)
REG32(LBIST_ISOLATION_EN, 0x20208)
    FIELD(LBIST_ISOLATION_EN, LPD_RPU, 1, 1)
    FIELD(LBIST_ISOLATION_EN, LPD, 0, 1)
REG32(LBIST_LOCK, 0x2020c)
    FIELD(LBIST_LOCK, LOCK, 0, 1)
REG32(LBIST_DONE, 0x20210)
    FIELD(LBIST_DONE, LPD_RPU, 1, 1)
    FIELD(LBIST_DONE, LPD, 0, 1)
REG32(LBIST_LPD_MISR_0, 0x20214)
REG32(LBIST_LPD_MISR_1, 0x20218)
REG32(LBIST_LPD_MISR_2, 0x2021c)
REG32(LBIST_LPD_MISR_3, 0x20220)
REG32(LBIST_LPD_MISR_4, 0x20224)
REG32(LBIST_LPD_MISR_5, 0x20228)
REG32(LBIST_LPD_MISR_6, 0x2022c)
REG32(LBIST_LPD_MISR_7, 0x20230)
REG32(LBIST_LPD_MISR_8, 0x20234)
REG32(LBIST_LPD_MISR_9, 0x20238)
REG32(LBIST_LPD_MISR_10, 0x2023c)
REG32(LBIST_LPD_MISR_11, 0x20240)
REG32(LBIST_LPD_MISR_12, 0x20244)
REG32(LBIST_LPD_MISR_13, 0x20248)
REG32(LBIST_LPD_RPU_MISR_0, 0x20250)
REG32(LBIST_LPD_RPU_MISR_1, 0x20254)
REG32(LBIST_LPD_RPU_MISR_2, 0x20258)

#define PMX_ANLG_R_MAX (R_LBIST_LPD_RPU_MISR_2 + 1)

REG32(PMXC_MEM_CLEAR_TRIGGER, 0x20100)
    FIELD(PMXC_MEM_CLEAR_TRIGGER, LPD_ASU, 14, 1)
    FIELD(PMXC_MEM_CLEAR_TRIGGER, LPD_RPU_E, 13, 1)
    FIELD(PMXC_MEM_CLEAR_TRIGGER, LPD_RPU_D, 12, 1)
    FIELD(PMXC_MEM_CLEAR_TRIGGER, LPD_RPU_C, 11, 1)
    FIELD(PMXC_MEM_CLEAR_TRIGGER, LPD_RPU_B, 10, 1)
    FIELD(PMXC_MEM_CLEAR_TRIGGER, LPD_RPU_A, 9, 1)
    FIELD(PMXC_MEM_CLEAR_TRIGGER, LPD_IOU, 6, 1)
    FIELD(PMXC_MEM_CLEAR_TRIGGER, LPD_RPU, 5, 1)
    FIELD(PMXC_MEM_CLEAR_TRIGGER, LPD, 4, 1)
    FIELD(PMXC_MEM_CLEAR_TRIGGER, PMC_IOU, 1, 1)
    FIELD(PMXC_MEM_CLEAR_TRIGGER, PMC, 0, 1)
REG32(PMXC_MEM_CLEAR_DONE, 0x20110)
    FIELD(PMXC_MEM_CLEAR_DONE, LPD_ASU, 14, 1)
    FIELD(PMXC_MEM_CLEAR_DONE, LPD_RPU_E, 13, 1)
    FIELD(PMXC_MEM_CLEAR_DONE, LPD_RPU_D, 12, 1)
    FIELD(PMXC_MEM_CLEAR_DONE, LPD_RPU_C, 11, 1)
    FIELD(PMXC_MEM_CLEAR_DONE, LPD_RPU_B, 10, 1)
    FIELD(PMXC_MEM_CLEAR_DONE, LPD_RPU_A, 9, 1)
    FIELD(PMXC_MEM_CLEAR_DONE, LPD_IOU, 6, 1)
    FIELD(PMXC_MEM_CLEAR_DONE, LPD_RPU, 5, 1)
    FIELD(PMXC_MEM_CLEAR_DONE, LPD, 4, 1)
    FIELD(PMXC_MEM_CLEAR_DONE, PMC_IOU, 1, 1)
    FIELD(PMXC_MEM_CLEAR_DONE, PMC, 0, 1)
REG32(PMXC_MEM_CLEAR_PASS, 0x20114)
    FIELD(PMXC_MEM_CLEAR_PASS, LPD_ASU, 14, 1)
    FIELD(PMXC_MEM_CLEAR_PASS, LPD_RPU_E, 13, 1)
    FIELD(PMXC_MEM_CLEAR_PASS, LPD_RPU_D, 12, 1)
    FIELD(PMXC_MEM_CLEAR_PASS, LPD_RPU_C, 11, 1)
    FIELD(PMXC_MEM_CLEAR_PASS, LPD_RPU_B, 10, 1)
    FIELD(PMXC_MEM_CLEAR_PASS, LPD_RPU_A, 9, 1)
    FIELD(PMXC_MEM_CLEAR_PASS, LPD_IOU, 6, 1)
    FIELD(PMXC_MEM_CLEAR_PASS, LPD_RPU, 5, 1)
    FIELD(PMXC_MEM_CLEAR_PASS, LPD, 4, 1)
    FIELD(PMXC_MEM_CLEAR_PASS, PMC_IOU, 1, 1)
    FIELD(PMXC_MEM_CLEAR_PASS, PMC, 0, 1)
REG32(PMXC_SCAN_CLEAR_TRIGGER, 0x20120)
    FIELD(PMXC_SCAN_CLEAR_TRIGGER, LPD_ASU, 14, 1)
    FIELD(PMXC_SCAN_CLEAR_TRIGGER, LPD_RPU_E, 13, 1)
    FIELD(PMXC_SCAN_CLEAR_TRIGGER, LPD_RPU_D, 12, 1)
    FIELD(PMXC_SCAN_CLEAR_TRIGGER, LPD_RPU_C, 11, 1)
    FIELD(PMXC_SCAN_CLEAR_TRIGGER, LPD_RPU_B, 10, 1)
    FIELD(PMXC_SCAN_CLEAR_TRIGGER, LPD_RPU_A, 9, 1)
    FIELD(PMXC_SCAN_CLEAR_TRIGGER, NOC, 8, 1)
    FIELD(PMXC_SCAN_CLEAR_TRIGGER, LPD_IOU, 6, 1)
    FIELD(PMXC_SCAN_CLEAR_TRIGGER, LPD_RPU, 5, 1)
    FIELD(PMXC_SCAN_CLEAR_TRIGGER, LPD, 4, 1)
REG32(PMXC_SCAN_CLEAR_LOCK, 0x20124)
    FIELD(PMXC_SCAN_CLEAR_LOCK, LOCK, 0, 1)
REG32(PMXC_SCAN_CLEAR_DONE, 0x20128)
    FIELD(PMXC_SCAN_CLEAR_DONE, LPD_ASU, 14, 1)
    FIELD(PMXC_SCAN_CLEAR_DONE, LPD_RPU_E, 13, 1)
    FIELD(PMXC_SCAN_CLEAR_DONE, LPD_RPU_D, 12, 1)
    FIELD(PMXC_SCAN_CLEAR_DONE, LPD_RPU_C, 11, 1)
    FIELD(PMXC_SCAN_CLEAR_DONE, LPD_RPU_B, 10, 1)
    FIELD(PMXC_SCAN_CLEAR_DONE, LPD_RPU_A, 9, 1)
    FIELD(PMXC_SCAN_CLEAR_DONE, LPD_IOU, 6, 1)
    FIELD(PMXC_SCAN_CLEAR_DONE, LPD_RPU, 5, 1)
    FIELD(PMXC_SCAN_CLEAR_DONE, LPD, 4, 1)
    FIELD(PMXC_SCAN_CLEAR_DONE, PMC, 0, 1)
REG32(PMXC_SCAN_CLEAR_PASS, 0x2012c)
    FIELD(PMXC_SCAN_CLEAR_PASS, LPD_ASU, 14, 1)
    FIELD(PMXC_SCAN_CLEAR_PASS, LPD_RPU_E, 13, 1)
    FIELD(PMXC_SCAN_CLEAR_PASS, LPD_RPU_D, 12, 1)
    FIELD(PMXC_SCAN_CLEAR_PASS, LPD_RPU_C, 11, 1)
    FIELD(PMXC_SCAN_CLEAR_PASS, LPD_RPU_B, 10, 1)
    FIELD(PMXC_SCAN_CLEAR_PASS, LPD_RPU_A, 9, 1)
    FIELD(PMXC_SCAN_CLEAR_PASS, LPD_IOU, 6, 1)
    FIELD(PMXC_SCAN_CLEAR_PASS, LPD_RPU, 5, 1)
    FIELD(PMXC_SCAN_CLEAR_PASS, LPD, 4, 1)
    FIELD(PMXC_SCAN_CLEAR_PASS, PMC, 0, 1)

typedef struct PMX_ANLG {
    SysBusDevice parent_obj;
    MemoryRegion iomem;
    qemu_irq irq_pmc_anlg_imr;
    qemu_irq irq_glitch_detected;

    Object *tamper_sink;

    uint32_t regs[PMX_ANLG_R_MAX];
    RegisterInfo regs_info[PMX_ANLG_R_MAX];
} PMX_ANLG;

static void pmc_anlg_imr_update_irq(PMX_ANLG *s)
{
    bool pending = s->regs[R_PMC_ANLG_ISR] & ~s->regs[R_PMC_ANLG_IMR];
    qemu_set_irq(s->irq_pmc_anlg_imr, pending);
}

static void pmc_anlg_isr_postw(RegisterInfo *reg, uint64_t val64)
{
    PMX_ANLG *s = XILINX_PMX_ANLG(reg->opaque);
    pmc_anlg_imr_update_irq(s);
}

static uint64_t pmc_anlg_ier_prew(RegisterInfo *reg, uint64_t val64)
{
    PMX_ANLG *s = XILINX_PMX_ANLG(reg->opaque);
    uint32_t val = val64;

    s->regs[R_PMC_ANLG_IMR] &= ~val;
    pmc_anlg_imr_update_irq(s);
    return 0;
}

static uint64_t pmc_anlg_idr_prew(RegisterInfo *reg, uint64_t val64)
{
    PMX_ANLG *s = XILINX_PMX_ANLG(reg->opaque);
    uint32_t val = val64;

    s->regs[R_PMC_ANLG_IMR] |= val;
    pmc_anlg_imr_update_irq(s);
    return 0;
}

static void bisr_cache_ctrl_0_postw(RegisterInfo *reg, uint64_t val64)
{
    PMX_ANLG *s = XILINX_PMX_ANLG(reg->opaque);

    if (ARRAY_FIELD_EX32(s->regs, BISR_CACHE_CTRL_0, TRIGGER)) {
        ARRAY_FIELD_DP32(s->regs, BISR_CACHE_STATUS, DONE, 1);
        ARRAY_FIELD_DP32(s->regs, BISR_CACHE_STATUS, PASS, 1);
    }
}

static void mem_clear_trigger_postw(RegisterInfo *reg, uint64_t val64)
{
    PMX_ANLG *s = XILINX_PMX_ANLG(reg->opaque);

    if (object_dynamic_cast(OBJECT(s), TYPE_XILINX_PMXC_ANLG)) {
        s->regs[R_PMXC_MEM_CLEAR_DONE] |= s->regs[R_PMXC_MEM_CLEAR_TRIGGER];
        s->regs[R_PMXC_MEM_CLEAR_PASS] |= s->regs[R_PMXC_MEM_CLEAR_TRIGGER];
    } else {
        s->regs[R_MEM_CLEAR_DONE] |= s->regs[R_MEM_CLEAR_TRIGGER];
        s->regs[R_MEM_CLEAR_PASS] |= s->regs[R_MEM_CLEAR_TRIGGER];
    }
}

static void scan_clear_trigger_postw(RegisterInfo *reg, uint64_t val64)
{
    PMX_ANLG *s = XILINX_PMX_ANLG(reg->opaque);

    s->regs[R_SCAN_CLEAR_DONE] |=
            ARRAY_FIELD_EX32(s->regs, SCAN_CLEAR_TRIGGER, LPD) <<
            R_SCAN_CLEAR_DONE_LPD_SHIFT;
    s->regs[R_SCAN_CLEAR_DONE] |=
            ARRAY_FIELD_EX32(s->regs, SCAN_CLEAR_TRIGGER, LPD_RPU) <<
            R_SCAN_CLEAR_DONE_LPD_RPU_SHIFT;
    s->regs[R_SCAN_CLEAR_DONE] |=
            ARRAY_FIELD_EX32(s->regs, SCAN_CLEAR_TRIGGER, LPD_IOU) <<
            R_SCAN_CLEAR_DONE_LPD_IOU_SHIFT;

    if (object_dynamic_cast(OBJECT(s), TYPE_XILINX_PMXC_ANLG)) {
        s->regs[R_PMXC_SCAN_CLEAR_DONE] |=
                ARRAY_FIELD_EX32(s->regs, PMXC_SCAN_CLEAR_TRIGGER, LPD_RPU_A) <<
                R_PMXC_SCAN_CLEAR_DONE_LPD_RPU_A_SHIFT;
        s->regs[R_PMXC_SCAN_CLEAR_DONE] |=
                ARRAY_FIELD_EX32(s->regs, PMXC_SCAN_CLEAR_TRIGGER, LPD_RPU_B) <<
                R_PMXC_SCAN_CLEAR_DONE_LPD_RPU_B_SHIFT;
        s->regs[R_PMXC_SCAN_CLEAR_DONE] |=
                ARRAY_FIELD_EX32(s->regs, PMXC_SCAN_CLEAR_TRIGGER, LPD_RPU_C) <<
                R_PMXC_SCAN_CLEAR_DONE_LPD_RPU_C_SHIFT;
        s->regs[R_PMXC_SCAN_CLEAR_DONE] |=
                ARRAY_FIELD_EX32(s->regs, PMXC_SCAN_CLEAR_TRIGGER, LPD_RPU_D) <<
                R_PMXC_SCAN_CLEAR_DONE_LPD_RPU_D_SHIFT;
        s->regs[R_PMXC_SCAN_CLEAR_DONE] |=
                ARRAY_FIELD_EX32(s->regs, PMXC_SCAN_CLEAR_TRIGGER, LPD_RPU_E) <<
                R_PMXC_SCAN_CLEAR_DONE_LPD_RPU_E_SHIFT;
        s->regs[R_PMXC_SCAN_CLEAR_DONE] |=
                ARRAY_FIELD_EX32(s->regs, PMXC_SCAN_CLEAR_TRIGGER, LPD_ASU) <<
                R_PMXC_SCAN_CLEAR_DONE_LPD_ASU_SHIFT;
    } else {
        s->regs[R_SCAN_CLEAR_DONE] |=
                ARRAY_FIELD_EX32(s->regs, SCAN_CLEAR_TRIGGER, LPD_RPU_A) <<
                R_SCAN_CLEAR_DONE_LPD_RPU_A_SHIFT;
        s->regs[R_SCAN_CLEAR_DONE] |=
                ARRAY_FIELD_EX32(s->regs, SCAN_CLEAR_TRIGGER, LPD_RPU_B) <<
                R_SCAN_CLEAR_DONE_LPD_RPU_B_SHIFT;
    }
    s->regs[R_SCAN_CLEAR_DONE] |=
               ARRAY_FIELD_EX32(s->regs, SCAN_CLEAR_TRIGGER, NOC) <<
               R_SCAN_CLEAR_DONE_PMC_SHIFT;

    s->regs[R_SCAN_CLEAR_PASS] |=
               ARRAY_FIELD_EX32(s->regs, SCAN_CLEAR_TRIGGER, LPD) <<
               R_SCAN_CLEAR_PASS_LPD_SHIFT;
    s->regs[R_SCAN_CLEAR_PASS] |=
               ARRAY_FIELD_EX32(s->regs, SCAN_CLEAR_TRIGGER, LPD_RPU) <<
               R_SCAN_CLEAR_PASS_LPD_RPU_SHIFT;
    s->regs[R_SCAN_CLEAR_PASS] |=
               ARRAY_FIELD_EX32(s->regs, SCAN_CLEAR_TRIGGER, LPD_IOU) <<
               R_SCAN_CLEAR_PASS_LPD_IOU_SHIFT;

    if (object_dynamic_cast(OBJECT(s), TYPE_XILINX_PMXC_ANLG)) {
        s->regs[R_PMXC_SCAN_CLEAR_PASS] |=
                ARRAY_FIELD_EX32(s->regs, PMXC_SCAN_CLEAR_TRIGGER, LPD_RPU_A) <<
                R_PMXC_SCAN_CLEAR_PASS_LPD_RPU_A_SHIFT;
        s->regs[R_PMXC_SCAN_CLEAR_PASS] |=
                ARRAY_FIELD_EX32(s->regs, PMXC_SCAN_CLEAR_TRIGGER, LPD_RPU_B) <<
                R_PMXC_SCAN_CLEAR_PASS_LPD_RPU_B_SHIFT;
        s->regs[R_PMXC_SCAN_CLEAR_PASS] |=
                ARRAY_FIELD_EX32(s->regs, PMXC_SCAN_CLEAR_TRIGGER, LPD_RPU_C) <<
                R_PMXC_SCAN_CLEAR_PASS_LPD_RPU_C_SHIFT;
        s->regs[R_PMXC_SCAN_CLEAR_PASS] |=
                ARRAY_FIELD_EX32(s->regs, PMXC_SCAN_CLEAR_TRIGGER, LPD_RPU_D) <<
                R_PMXC_SCAN_CLEAR_PASS_LPD_RPU_D_SHIFT;
        s->regs[R_PMXC_SCAN_CLEAR_PASS] |=
                ARRAY_FIELD_EX32(s->regs, PMXC_SCAN_CLEAR_TRIGGER, LPD_RPU_E) <<
                R_PMXC_SCAN_CLEAR_PASS_LPD_RPU_E_SHIFT;
        s->regs[R_PMXC_SCAN_CLEAR_PASS] |=
                ARRAY_FIELD_EX32(s->regs, PMXC_SCAN_CLEAR_TRIGGER, LPD_ASU) <<
                R_PMXC_SCAN_CLEAR_PASS_LPD_ASU_SHIFT;
    } else {
        s->regs[R_SCAN_CLEAR_PASS] |=
                ARRAY_FIELD_EX32(s->regs, SCAN_CLEAR_TRIGGER, LPD_RPU_A) <<
                R_SCAN_CLEAR_PASS_LPD_RPU_A_SHIFT;
        s->regs[R_SCAN_CLEAR_PASS] |=
                ARRAY_FIELD_EX32(s->regs, SCAN_CLEAR_TRIGGER, LPD_RPU_B) <<
                R_SCAN_CLEAR_PASS_LPD_RPU_B_SHIFT;
    }
    s->regs[R_SCAN_CLEAR_PASS] |=
               ARRAY_FIELD_EX32(s->regs, SCAN_CLEAR_TRIGGER, NOC) <<
               R_SCAN_CLEAR_PASS_PMC_SHIFT;
}

static void pmx_anlg_clear_gd_status(PMX_ANLG *s)
{
    if (ARRAY_FIELD_EX32(s->regs, GD_CTRL, GD1_RST_STATUS_REG)) {
        ARRAY_FIELD_DP32(s->regs, GLITCH_DET_STATUS, VCCINT_PMC_1, 0);
    }
    if (ARRAY_FIELD_EX32(s->regs, GD_CTRL, GD0_RST_STATUS_REG)) {
        ARRAY_FIELD_DP32(s->regs, GLITCH_DET_STATUS, VCCINT_PMC_0, 0);
    }
}

static void pmx_anlg_tamper_out(PMX_ANLG *s, uint32_t events)
{
    const char *name = XLNX_AMS_TAMPER_PROP;
    g_autofree Error *err = NULL;

    if (!events || !s->tamper_sink) {
        return;
    }

    object_property_set_uint(s->tamper_sink, name, events, &err);
    if (err) {
        g_autofree char *p_dev = object_get_canonical_path(OBJECT(s));
        g_autofree char *p_qom = object_get_canonical_path(s->tamper_sink);

        warn_report("%s: qom-set %s %s 0x%02x failed: %s", p_dev, p_qom,
                    name, events, error_get_pretty(err));
    }
}

static void pmx_anlg_set_gd_status(PMX_ANLG *s, unsigned bits)
{
    uint32_t ctrl = s->regs[R_GD_CTRL];
    uint32_t tamper;
    unsigned n;

    static const struct gd_mask {
        uint32_t status;
        uint32_t dis;
        uint32_t rst;
        uint32_t tamper;
    } gd_mask[] = {
        [0] = { R_GLITCH_DET_STATUS_VCCINT_PMC_0_MASK,
                R_GD_CTRL_GD0_EN_GLITCH_DET_B_MASK,
                R_GD_CTRL_GD0_RST_STATUS_REG_MASK,
                XLNX_AMS_VCCINT_0_GLITCH_MASK },

        [1] = { R_GLITCH_DET_STATUS_VCCINT_PMC_1_MASK,
                R_GD_CTRL_GD1_EN_GLITCH_DET_B_MASK,
                R_GD_CTRL_GD1_RST_STATUS_REG_MASK,
                XLNX_AMS_VCCINT_1_GLITCH_MASK },
    };

    for (tamper = 0, n = 0; n < ARRAY_SIZE(gd_mask); n++) {
        if (!(bits & gd_mask[n].status)) {
            continue;   /* no glitch */
        }
        if (ctrl & gd_mask[n].rst) {
            continue;   /* detector in reset */
        }
        if (ctrl & gd_mask[n].dis) {
            continue;   /* detector disabled */
        }

        s->regs[R_GLITCH_DET_STATUS] |= gd_mask[n].status;
        tamper |= gd_mask[n].tamper;
    }

    if (tamper) {
        /* Both outputs are non-maskable */
        pmx_anlg_tamper_out(s, tamper);
        qemu_irq_pulse(s->irq_glitch_detected);
    }
}

static void pmx_anlg_inject_glitches(PMX_ANLG *s)
{
    uint32_t ctrl = s->regs[R_GD_CTRL];
    uint32_t gd;
    unsigned n;

    static const struct test_mask {
        uint32_t gd;
        uint32_t mode;
        uint32_t sel;
        uint32_t gen;
    } test_mask[] = {
        [0] = { R_GLITCH_DET_STATUS_VCCINT_PMC_0_MASK,
                R_GD_CTRL_GD0_TEST_GLITCH_SEL_MASK,
                R_GD_CTRL_GD0_TEST_GLITCH_GEN_MASK,
                R_GD_CTRL_GD0_GL_DET_TEST_MODE_MASK },

        [1] = { R_GLITCH_DET_STATUS_VCCINT_PMC_1_MASK,
                R_GD_CTRL_GD1_TEST_GLITCH_SEL_MASK,
                R_GD_CTRL_GD1_TEST_GLITCH_GEN_MASK,
                R_GD_CTRL_GD1_GL_DET_TEST_MODE_MASK },
    };

    for (gd = 0, n = 0; n < ARRAY_SIZE(test_mask); n++) {
        if (!(ctrl & test_mask[n].gen)) {
            continue;   /* not injecting */
        }
        if (!(ctrl & test_mask[n].mode)) {
            continue;   /* not in test mode */
        }
        if (!(ctrl & test_mask[n].sel)) {
            continue;   /* test config selected */
        }

        gd |= test_mask[n].gd;
    }

    pmx_anlg_set_gd_status(s, gd);
}

static void pmx_anlg_gd_ctrl_postw(RegisterInfo *reg, uint64_t val64)
{
    PMX_ANLG *s = XILINX_PMX_ANLG(reg->opaque);

    pmx_anlg_clear_gd_status(s);
    pmx_anlg_inject_glitches(s);
}

static const RegisterAccessInfo pmx_anlg_regs_info[] = {
    {   .name = "GD_CTRL",  .addr = A_GD_CTRL,
        .rsvd = 0xfc00fc00,
        .post_write = pmx_anlg_gd_ctrl_postw,
    },{   .name = "GLITCH_DET_STATUS",  .addr = A_GLITCH_DET_STATUS,
        .rsvd = 0xfffffffc,
        .ro = 0x3,
    },{ .name = "VGG_CTRL",  .addr = A_VGG_CTRL,
        .rsvd = 0xfff8000c,
    },{ .name = "CFRM_PROBE",  .addr = A_CFRM_PROBE,
        .rsvd = 0xfff00000,
    },{ .name = "PMC_SYSMON",  .addr = A_PMC_SYSMON,
        .rsvd = 0xfffffffe,
    },{ .name = "GD_FUSE_CTRL_0",  .addr = A_GD_FUSE_CTRL_0,
        .rsvd = 0xfff080ce,
    },{ .name = "GD_FUSE_CTRL_1",  .addr = A_GD_FUSE_CTRL_1,
        .rsvd = 0xfff080ce,
    },{ .name = "CFG_POR_CNT_SKIP",  .addr = A_CFG_POR_CNT_SKIP,
    },{ .name = "PMC_ANLG_ISR",  .addr = A_PMC_ANLG_ISR,
        .w1c = 0x1,
        .post_write = pmc_anlg_isr_postw,
    },{ .name = "PMC_ANLG_IMR",  .addr = A_PMC_ANLG_IMR,
        .reset = 0x1,
        .ro = 0x1,
    },{ .name = "PMC_ANLG_IER",  .addr = A_PMC_ANLG_IER,
        .pre_write = pmc_anlg_ier_prew,
    },{ .name = "PMC_ANLG_IDR",  .addr = A_PMC_ANLG_IDR,
        .pre_write = pmc_anlg_idr_prew,
    },{ .name = "SLVERR_CTRL",  .addr = A_SLVERR_CTRL,
    },{ .name = "BNK3_VMODE",  .addr = A_BNK3_VMODE,
        .rsvd = 0xfffffffe,
        .ro = 0x1,
    },{ .name = "LDO_CALIB_TIME",  .addr = A_LDO_CALIB_TIME,
        .reset = 0x8fc,
    },{ .name = "BISR_CACHE_CTRL_0",  .addr = A_BISR_CACHE_CTRL_0,
        .rsvd = 0xe,
        .post_write = bisr_cache_ctrl_0_postw,
    },{ .name = "BISR_CACHE_STATUS",  .addr = A_BISR_CACHE_STATUS,
        .ro = 0x3,
    },{ .name = "MEM_CLEAR_TRIGGER",  .addr = A_MEM_CLEAR_TRIGGER,
        .rsvd = 0xc,
        .post_write = mem_clear_trigger_postw,
    },{ .name = "MEM_CLEAR_DONE",  .addr = A_MEM_CLEAR_DONE,
        .rsvd = 0xc,
        .ro = 0x1ff,
    },{ .name = "MEM_CLEAR_PASS",  .addr = A_MEM_CLEAR_PASS,
        .rsvd = 0xc,
        .ro = 0x1ff,
    },{ .name = "SCAN_CLEAR_TRIGGER",  .addr = A_SCAN_CLEAR_TRIGGER,
        .rsvd = 0x8f,
        .post_write = scan_clear_trigger_postw,
    },{ .name = "SCAN_CLEAR_LOCK",  .addr = A_SCAN_CLEAR_LOCK,
    },{ .name = "SCAN_CLEAR_DONE",  .addr = A_SCAN_CLEAR_DONE,
        .rsvd = 0x20e,
        .ro = 0x3ff,
    },{ .name = "SCAN_CLEAR_PASS",  .addr = A_SCAN_CLEAR_PASS,
        .rsvd = 0x20e,
        .ro = 0x3ff,
    },{ .name = "LBIST_ENABLE",  .addr = A_LBIST_ENABLE,
    },{ .name = "LBIST_RST_N",  .addr = A_LBIST_RST_N,
    },{ .name = "LBIST_ISOLATION_EN",  .addr = A_LBIST_ISOLATION_EN,
    },{ .name = "LBIST_LOCK",  .addr = A_LBIST_LOCK,
    },{ .name = "LBIST_DONE",  .addr = A_LBIST_DONE,
        .ro = 0x3,
        .reset = R_LBIST_DONE_LPD_MASK | R_LBIST_DONE_LPD_RPU_MASK,
    },{ .name = "LBIST_LPD_MISR_0",  .addr = A_LBIST_LPD_MISR_0,
        .ro = 0xffffffff,
    },{ .name = "LBIST_LPD_MISR_1",  .addr = A_LBIST_LPD_MISR_1,
        .ro = 0xffffffff,
    },{ .name = "LBIST_LPD_MISR_2",  .addr = A_LBIST_LPD_MISR_2,
        .ro = 0xffffffff,
    },{ .name = "LBIST_LPD_MISR_3",  .addr = A_LBIST_LPD_MISR_3,
        .ro = 0xffffffff,
    },{ .name = "LBIST_LPD_MISR_4",  .addr = A_LBIST_LPD_MISR_4,
        .ro = 0xffffffff,
    },{ .name = "LBIST_LPD_MISR_5",  .addr = A_LBIST_LPD_MISR_5,
        .ro = 0xffffffff,
    },{ .name = "LBIST_LPD_MISR_6",  .addr = A_LBIST_LPD_MISR_6,
        .ro = 0xffffffff,
    },{ .name = "LBIST_LPD_MISR_7",  .addr = A_LBIST_LPD_MISR_7,
        .ro = 0xffffffff,
    },{ .name = "LBIST_LPD_MISR_8",  .addr = A_LBIST_LPD_MISR_8,
        .ro = 0xffffffff,
    },{ .name = "LBIST_LPD_MISR_9",  .addr = A_LBIST_LPD_MISR_9,
        .ro = 0xffffffff,
    },{ .name = "LBIST_LPD_MISR_10",  .addr = A_LBIST_LPD_MISR_10,
        .ro = 0xffffffff,
    },{ .name = "LBIST_LPD_MISR_11",  .addr = A_LBIST_LPD_MISR_11,
        .ro = 0xffffffff,
    },{ .name = "LBIST_LPD_MISR_12",  .addr = A_LBIST_LPD_MISR_12,
        .ro = 0xffffffff,
    },{ .name = "LBIST_LPD_MISR_13",  .addr = A_LBIST_LPD_MISR_13,
        .ro = 0xffffffff,
    },{ .name = "LBIST_LPD_RPU_MISR_0",  .addr = A_LBIST_LPD_RPU_MISR_0,
        .ro = 0xffffffff,
    },{ .name = "LBIST_LPD_RPU_MISR_1",  .addr = A_LBIST_LPD_RPU_MISR_1,
        .ro = 0xffffffff,
    },{ .name = "LBIST_LPD_RPU_MISR_2",  .addr = A_LBIST_LPD_RPU_MISR_2,
        .ro = 0xffffffff,
    }
};

#define PMXC_ADDR(x) (x - A_PMXC_MEM_CLEAR_TRIGGER)
static const RegisterAccessInfo pmxc_anlg_regs_info[] = {
    {   .name = "PMXC_MEM_CLEAR_TRIGGER",
        .addr = PMXC_ADDR(A_PMXC_MEM_CLEAR_TRIGGER),
        .rsvd = 0x18c,
        .post_write = mem_clear_trigger_postw,
    },{ .name = "PMXC_MEM_CLEAR_DONE",
        .addr = PMXC_ADDR(A_PMXC_MEM_CLEAR_DONE),
        .rsvd = 0x18c,
        .ro = 0xffff,
    },{ .name = "PMXC_MEM_CLEAR_PASS",
        .addr = PMXC_ADDR(A_PMXC_MEM_CLEAR_PASS),
        .rsvd = 0x18c,
        .ro = 0xffff,
    },{ .name = "PMXC_SCAN_CLEAR_TRIGGER",
        .addr = PMXC_ADDR(A_PMXC_SCAN_CLEAR_TRIGGER),
        .rsvd = 0x8f,
        .post_write = scan_clear_trigger_postw,
    },{ .name = "PMXC_SCAN_CLEAR_LOCK",
        .addr = PMXC_ADDR(A_PMXC_SCAN_CLEAR_LOCK),
    },{ .name = "PMXC_SCAN_CLEAR_DONE",
        .addr = PMXC_ADDR(A_PMXC_SCAN_CLEAR_DONE),
        .rsvd = 0x1018e,
        .ro = 0x1ffff,
    },{ .name = "PMXC_SCAN_CLEAR_PASS",
        .addr = PMXC_ADDR(A_PMXC_SCAN_CLEAR_PASS),
        .rsvd = 0x1018e,
        .ro = 0x1ffff,
    }
};

static void pmx_anlg_reset_enter(Object *obj, ResetType type)
{
    PMX_ANLG *s = XILINX_PMX_ANLG(obj);
    unsigned int i;

    for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
        register_reset(&s->regs_info[i]);
    }
}

static void pmx_anlg_reset_hold(Object *obj)
{
    PMX_ANLG *s = XILINX_PMX_ANLG(obj);

    pmc_anlg_imr_update_irq(s);
}

static const MemoryRegionOps pmx_anlg_ops = {
    .read = register_read_memory,
    .write = register_write_memory,
    .endianness = DEVICE_LITTLE_ENDIAN,
    .valid = {
        .min_access_size = 4,
        .max_access_size = 4,
    },
};

static void pmxc_anlg_init(Object *obj)
{
    PMX_ANLG *s = XILINX_PMX_ANLG(obj);
    RegisterInfoArray *reg_array;

    reg_array =
        register_init_block32(DEVICE(obj), pmxc_anlg_regs_info,
                              ARRAY_SIZE(pmxc_anlg_regs_info),
                              &s->regs_info[R_PMXC_MEM_CLEAR_TRIGGER],
                              &s->regs[R_PMXC_MEM_CLEAR_TRIGGER],
                              &pmx_anlg_ops, XILINX_PMX_ANLG_ERR_DEBUG,
                              (R_PMXC_SCAN_CLEAR_PASS -
                               R_PMXC_MEM_CLEAR_TRIGGER + 1) * 4);
    memory_region_add_subregion_overlap(&s->iomem, A_MEM_CLEAR_TRIGGER,
                                        &reg_array->mem, 1);
}

static void pmx_anlg_init(Object *obj)
{
    PMX_ANLG *s = XILINX_PMX_ANLG(obj);
    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    RegisterInfoArray *reg_array;

    memory_region_init(&s->iomem, obj, TYPE_XILINX_PMX_ANLG,
                       PMX_ANLG_R_MAX * 4);
    reg_array =
        register_init_block32(DEVICE(obj), pmx_anlg_regs_info,
                              ARRAY_SIZE(pmx_anlg_regs_info),
                              s->regs_info, s->regs,
                              &pmx_anlg_ops,
                              XILINX_PMX_ANLG_ERR_DEBUG,
                              PMX_ANLG_R_MAX * 4);
    memory_region_add_subregion(&s->iomem,
                                0x0,
                                &reg_array->mem);
    sysbus_init_mmio(sbd, &s->iomem);

    qdev_init_gpio_out(DEVICE(obj), &s->irq_glitch_detected, 1);
    sysbus_init_irq(sbd, &s->irq_pmc_anlg_imr);
}

static Property pmx_anlg_properties[] = {
    DEFINE_PROP_LINK("tamper-sink", PMX_ANLG, tamper_sink,
                     TYPE_OBJECT, Object *),

    DEFINE_PROP_END_OF_LIST(),
};

static const VMStateDescription vmstate_pmx_anlg = {
    .name = TYPE_XILINX_PMX_ANLG,
    .version_id = 1,
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
        VMSTATE_UINT32_ARRAY(regs, PMX_ANLG, PMX_ANLG_R_MAX),
        VMSTATE_END_OF_LIST(),
    }
};

static void pmx_anlg_class_init(ObjectClass *klass, void *data)
{
    ResettableClass *rc = RESETTABLE_CLASS(klass);
    DeviceClass *dc = DEVICE_CLASS(klass);

    dc->vmsd = &vmstate_pmx_anlg;
    rc->phases.enter = pmx_anlg_reset_enter;
    rc->phases.hold = pmx_anlg_reset_hold;

    device_class_set_props(dc, pmx_anlg_properties);
}

static const TypeInfo pmxc_anlg_info = {
    .name           = TYPE_XILINX_PMXC_ANLG,
    .parent         = TYPE_XILINX_PMX_ANLG,
    .instance_init  = pmxc_anlg_init,
};

static const TypeInfo pmx_anlg_info = {
    .name          = TYPE_XILINX_PMX_ANLG,
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(PMX_ANLG),
    .class_init    = pmx_anlg_class_init,
    .instance_init = pmx_anlg_init,
};

static void pmx_anlg_register_types(void)
{
    type_register_static(&pmx_anlg_info);
    type_register_static(&pmxc_anlg_info);
}

type_init(pmx_anlg_register_types)
